[Flutter] Cupertinoでナビゲーションバーを実装する際に少しハマったこと
こんにちは、CX事業本部 IoT事業部の若槻です。
今回は、FlutterのCupertinoでナビゲーションバーを実装する際に少しハマったことについて共有します。
Cupertinoとは
Cupertinoを使用すると、FlutterアプリケーションにiOSスタイルのデザインのWidgetを導入することができます。
CupertinoスタイルのWidgetは様々なものが用意されており、既定のMaterial DesignスタイルのWidgetを置き換えて、iOSネイティブのようなデザインのUIを簡単に実装できます。
下記エントリでは、CupertinoとMaterial DesignのUIの実装を比較しているので合わせてご覧ください。
ハマったこと
さて、そんなCupertinoのCupertinoNavigationBar classを使用してナビゲーションバーを実装しようとしたところ、いくつかハマりどころがありました。
下記はドキュメントにあるサンプルコードです。このコードを例にハマった箇所について紹介します。
import 'package:flutter/cupertino.dart'; void main() => runApp(const NavBarApp()); class NavBarApp extends StatelessWidget { const NavBarApp({super.key}); @override Widget build(BuildContext context) { return const CupertinoApp( theme: CupertinoThemeData(brightness: Brightness.light), home: NavBarExample(), ); } } class NavBarExample extends StatefulWidget { const NavBarExample({super.key}); @override State<NavBarExample> createState() => _NavBarExampleState(); } class _NavBarExampleState extends State<NavBarExample> { @override Widget build(BuildContext context) { return CupertinoPageScaffold( navigationBar: CupertinoNavigationBar( backgroundColor: CupertinoColors.systemGrey.withOpacity(0.5), middle: const Text('CupertinoNavigationBar Sample'), ), child: Column( children: <Widget>[ Container(height: 50, color: CupertinoColors.systemRed), Container(height: 50, color: CupertinoColors.systemGreen), Container(height: 50, color: CupertinoColors.systemBlue), Container(height: 50, color: CupertinoColors.systemYellow), ], ), ); } }
アプリケーションをChromeで実行した様子。
デフォルトで背景色が透過となる
まず、CupertinoNavigationBarではデフォルトで背景色が透過され、オブジェクトがオーバーレイしてしまいます。
例えば次のようにbackgroundColor
を指定しない場合は既定の透過度0
となります。
navigationBar: const CupertinoNavigationBar( //backgroundColor: CupertinoColors.systemGrey.withOpacity(0.5), middle: Text('CupertinoNavigationBar Sample'), ),
透過させないようにするためには、次のように透過度1.0
を明示的に指定するか、透過なしのColorオブジェクトを指定するようにします。
navigationBar: CupertinoNavigationBar( backgroundColor: CupertinoColors.systemGrey.withOpacity(1.0), middle: const Text('CupertinoNavigationBar Sample'), ),
navigationBar: CupertinoNavigationBar( backgroundColor: CupertinoColors.systemGrey, middle: const Text('CupertinoNavigationBar Sample'), ),
別Widget化する場合は ObstructingPreferredSizeWidget で implements する
クラスを再利用出来るように別Widget化したい場合があると思います。そこでCupertinoNavigationBar
を使用して次のようなCommonNavigationBar
Widgetを実装しました。
class _NavBarExampleState extends State<NavBarExample> { @override Widget build(BuildContext context) { return CupertinoPageScaffold( navigationBar: const CommonNavigationBar('CupertinoNavigationBar Sample'), child: Column( children: <Widget>[ Container(height: 50, color: CupertinoColors.systemRed), Container(height: 50, color: CupertinoColors.systemGreen), Container(height: 50, color: CupertinoColors.systemBlue), Container(height: 50, color: CupertinoColors.systemYellow), ], ), ); } } class CommonNavigationBar extends StatelessWidget { final String middleText; const CommonNavigationBar({Key? key, this.middleText = ''}) : super(key: key); @override Widget build(BuildContext context) { return CupertinoNavigationBar( backgroundColor: CupertinoColors.systemGrey, middle: Text(middleText), ); } }
しかし上記実装は次のようなエラーとなってしまいます。
原因としては、エラーメッセージの通りなのですが、CupertinoPageScaffold.navigationBar
に指定可能なObstructingPreferredSizeWidget
以外のタイプが指定されたためです。
The argument type 'CommonNavigationBar' can't be assigned to the parameter type 'ObstructingPreferredSizeWidget?'
ObstructingPreferredSizeWidgetクラスでは、Widgetの優先サイズを取得するgetterであるPreferredSizeWidget.preferredSize
と、透過の有無を返すObstructingPreferredSizeWidget.shouldFullyObstruct
の実装が必要となります。
そこでCommonNavigationBar
を次のように修正し、ObstructingPreferredSizeWidget
を継承したWidgetを実装します。
class _NavBarExampleState extends State<NavBarExample> { @override Widget build(BuildContext context) { return CupertinoPageScaffold( navigationBar: const CommonNavigationBar( middleText: 'CupertinoNavigationBar Sample'), child: Column( children: <Widget>[ Container(height: 50, color: CupertinoColors.systemRed), Container(height: 50, color: CupertinoColors.systemGreen), Container(height: 50, color: CupertinoColors.systemBlue), Container(height: 50, color: CupertinoColors.systemYellow), ], ), ); } } class CommonNavigationBar extends StatelessWidget implements ObstructingPreferredSizeWidget { final String middleText; const CommonNavigationBar({Key? key, this.middleText = ''}) : super(key: key); @override Widget build(BuildContext context) { return CupertinoNavigationBar( backgroundColor: CupertinoColors.systemGrey, middle: Text(middleText), ); } @override Size get preferredSize => const Size.fromHeight(40); // Widgetの高さを指定 @override bool shouldFullyObstruct(BuildContext context) { return true; // 透過させない場合はtrue } }
これで無事CupertinoNavigationBar
を別Widget化できました。
おわりに
FlutterのCupertinoでナビゲーションバーを実装する際に少しハマったことについて共有しました。
公式ドキュメントをよく読みましょうということに尽きると思います。
以上